home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------
- #
- # NewsWatcher - Macintosh NNTP Client Application
- #
- # Written by Steven Falkenburg
- # ©1990 Apple Computer, Inc.
- #
- #-----------------------------------------------------------
- #
- # nntplow.c
- #
- # The nntplow code module contains the network code which
- # communicates with the remote NNTP server to get/post
- # news articles or retrieve subject or group lists.
- #
- #-----------------------------------------------------------*/
-
- #pragma segment lowlevel
-
- #include "compat.h"
-
- #ifdef PROTOS
-
- #include <Types.h>
- #include <Memory.h>
- #include <QuickDraw.h>
- #include <OSUtils.h>
- #include <CursorCtl.h>
- #include <Packages.h>
- #include <ToolUtils.h>
- #include <Lists.h>
- #include <Strings.h>
-
- #endif
-
- #include <string.h>
-
- #include "nntp.h"
- #include "MacTCPCommonTypes.h"
- #include "TCPPB.h"
- #include "netstuff.h"
- #include "TCPHi.h"
- #include "FTPLow.h"
- #include "NNTPLow.h"
- #include "miscstuff.h"
-
- unsigned long gConnID = 0; /* connection id for nntp connection */
- short gNumGroups; /* number of newsgroups */
- TGroup *gGroupList; /* pointer to first group */
- static TGroup **groupHandle; /* handle to first group */
- unsigned long gNNTPAddress; /* address of NNTP server */
-
- OSErr GrowDataStructure(void);
-
-
- /* GrowDataStructure dynamically grows the group data structure, re-assigning
- gGroupList after locking the handle.
- */
-
- OSErr GrowDataStructure(void)
- {
- Size prevSize;
- OSErr err;
-
- HUnlock((Handle)groupHandle);
- prevSize = GetHandleSize((Handle)groupHandle);
- MySetHandleSize((Handle)groupHandle,prevSize+sizeof(TGroup));
- if ((err = MyMemErr())!=noErr) {
- MoveHHi((Handle)groupHandle);
- HLock((Handle)groupHandle);
- gGroupList = *groupHandle;
- return err;
- }
- MoveHHi((Handle)groupHandle);
- HLock((Handle)groupHandle);
- gGroupList = *groupHandle;
- }
-
-
- /* StartNNTP initializes MacTCP, opens the NNTP connection, and
- gets the list of groups from the NNTP server.
- */
-
- OSErr StartNNTP(void)
- {
- OSErr err;
-
- StatusWindow("Getting NNTP Address...",-1);
- GetConfiguration();
-
- StatusWindow("Opening TCP Driver...",-1);
- GiveTime(0);
- if ((err = InitNNTP()) == noErr &&
- (err = FTPInit((ProcPtr)StatusProc)) == noErr &&
- (StatusWindow("Opening Connection...",-1)) &&
- (err = OpenNewsConnection(gNNTPAddress)) == noErr &&
- (StatusWindow("Getting Groups From Net...",0)) &&
- (err = GetGroupList(&gNumGroups)) == noErr &&
- (StatusWindow("Getting Groups From Net...",100)))
- ;
- GiveTime(0);
- return err;
- }
-
-
- /* InitNNTP is called to initialize all drivers/etc necessary for
- operation and maintenence of the NNTP connection.
- */
-
- OSErr InitNNTP(void)
- {
- groupHandle = (TGroup **)MyNewHandle(sizeof(TGroup));
- if (MyMemErr() != noErr)
- return MyMemErr();
- HLock((Handle)groupHandle);
- gGroupList = *groupHandle;
- return InitNetwork();
- }
-
-
- /* ResetConnection closes and re-opens the NNTP connection. This routine
- is called when a network error occurs.
- */
-
- OSErr ResetConnection(void)
- {
- OSErr err;
- Str255 tmpStr;
- extern Boolean gCancel;
-
- gCancel = false;
- AbortNewsConnection();
- StatusWindow("Re-establishing Connection...",-1);
- if ((err = InitNetwork()) == noErr &&
- (err = FTPInit((ProcPtr)StatusProc)) == noErr &&
- (err = OpenNewsConnection(gNNTPAddress)) == noErr)
- ;
- else {
- NumToString(err,tmpStr);
- ParamText("\pConnection could not established.",tmpStr,"\p","\p");
- CautionAlert(kErrDlg,nil);
- }
- return err;
- }
-
-
- /* GetHello gets the initial status back from an open NNTP connection
- to determine whether or not the connection was opened sucessfully.
- */
-
- OSErr GetHello(void)
- {
- OSErr err;
- Ptr data;
- unsigned short length;
-
- data = MyNewPtr(kBufLen);
- if (MyMemErr() != noErr)
- return MyMemErr();
-
- length = kBufLen;
-
- err = RecvData(gConnID,data,&length,true);
-
- if (*data != '2')
- err = -1;
-
- MyDisposPtr(data);
-
- return err;
- }
-
-
- /* OpenNewsConnection opens the conneciton to the remote NNTP server.
- */
-
- OSErr OpenNewsConnection(unsigned long address)
- {
- unsigned long recvLen;
- OSErr err;
-
- recvLen = kBufLen;
- if ((err = CreateStream(&gConnID,recvLen)) != noErr)
- return err;
- if ((err = OpenConnection(gConnID,address,kNNTPPort,10)) == noErr)
- err = GetHello();
-
- return err;
- }
-
-
- /* CloseConnection closes the connection to the NNTP server. This is normally
- called when the program is terminating.
- */
-
- OSErr CloseNewsConnection(void)
- {
- OSErr err;
-
- StatusWindow("Closing NNTP Connection...",-1);
- err = CloseConnection(gConnID);
- err = ReleaseStream(gConnID);
- return err;
- }
-
-
- /* AbortNewsConnection forcibly closes a connection to the NNTP server, usually
- before resetting the connection.
- */
-
- OSErr AbortNewsConnection(void)
- {
- StatusWindow("Aborting NNTP Connection...",-1);
- AbortConnection(gConnID);
- return ReleaseStream(gConnID);
- }
-
-
- /* GetGroupList gets the list of all known newsgroups from the NNTP server.
- */
-
- OSErr GetGroupList(short *numGroups)
- {
- OSErr err;
- char *current;
- char curGroup[256];
- char *data;
- short nameLen = 0;
- unsigned short length = (unsigned short) kBufLen;
- Boolean done = false,start = true;
- short state = cNormal;
- Str255 sendData[2];
-
- *numGroups = 0;
- nameLen = 0;
-
- strcpy((char *)sendData[0],"LIST");
- strcpy((char *)sendData[1],CRLF);
-
- if ((err = SendMultiData(gConnID,sendData,2,true)) != noErr) {
- ResetConnection();
- return err;
- }
-
- data = MyNewPtr(kBufLen);
- if (MyMemErr() != noErr)
- return MyMemErr();
-
- while ( !done && ((err = RecvData(gConnID,data,&length,true)) == noErr)) {
- for (current = data; (current - data) < length; current++) {
- switch (*current) {
- case CR:
- if (state == cLastDot)
- state = cLastDotCR;
- else
- state = cLastCR;
- break;
- case LF:
- if (state == cLastCR) {
- state = cLastLF;
-
- curGroup[nameLen] = '\0';
- if ((err = GrowDataStructure()) != noErr)
- return err;
- if (ParseGroup(curGroup,nameLen,&gGroupList[*numGroups])) {
- start = false;
- (*numGroups)++;
- StatusWindow("Getting Groups From Net...",(short)1+(((float)(*numGroups)/1100.0)*100));
- }
- else if (start==true && curGroup[0]!='2') {
- ResetConnection();
- done=true;
- }
- nameLen = 0;
- GiveTime(0);
- }
- else if (state == cLastDotCR) {
- done = true;
- state = cLastDotCRLF;
- }
- break;
- case '.':
- if (state == cLastLF)
- state = cLastDot;
- else {
- state = cNormal;
- curGroup[nameLen++] = *current;
- }
- break;
- default:
- state = cNormal;
- curGroup[nameLen++] = *current;
- break;
- }
- }
- length = (unsigned short) kBufLen;
- }
- MyDisposPtr(data);
- SetCursor(&QDARROW);
-
- if (err != noErr)
- ResetConnection();
-
- return noErr;
- }
-
-
- /* ParseGroup parses out the name of a specific group from the receive buffer.
- This is called once per line for the buffer received after a 'LIST' command.
- */
-
- Boolean ParseGroup(char *groupString,short groupLen,TGroup *groupStorage)
- {
- char tmpStr[256],tmpStore[512];
- char *current,*tmpCurrent;
-
- if (groupLen > 0 &&
- !(*groupString >= '0' && *groupString <= '9') &&
- *groupString != '.') {
- if (MyMemErr() != noErr)
- return false;
- for (current = groupString,tmpCurrent=tmpStore;
- *current != ' ' && *current;
- *tmpCurrent++ = *current++);
- *tmpCurrent = '\0';
- strncpy(groupStorage->name,tmpStore,kGNameLen);
- groupStorage->name[kGNameLen-1] = '\0';
- for (current++,tmpCurrent=tmpStr;
- *current != ' ' && *current;
- *tmpCurrent++ = *current++);
- *tmpCurrent = '\0';
- StringToNum((StringPtr)c2pstr(tmpStr),&groupStorage->lastMess);
- if (groupStorage->lastMess == 0)
- groupStorage->lastMess++;
-
- for (current++,tmpCurrent=tmpStr;
- *current != ' ' && *current;
- *tmpCurrent++ = *current++);
- *tmpCurrent = '\0';
- StringToNum((StringPtr)c2pstr(tmpStr),&groupStorage->firstMess);
- if (groupStorage->firstMess == 0)
- groupStorage->firstMess++;
-
- groupStorage->status = *(current+1);
-
- if (groupStorage->status == 'n')
- return false;
-
- return true;
- }
- else
- return false;
- }
-
-
- /* GetMessages returns the list of subjects from a range of messages in a
- specific newsgroup. This routine issues the command 'XHDR <first>-<last>'
- to the remote NNTP server.
- */
-
- OSErr GetMessages(char *newsGroup,long first,long last,TSubject *subjects,long *numSubjects,char *hdrName)
- {
- Ptr data;
- Boolean done = false,start;
- OSErr err;
- unsigned short length;
- char *current,*current2,*current3,curSubject[1024],numberStr[256];
- short nameLen;
- short state = cNormal;
- char tmpStr[256];
- long articleNumber;
- Str255 sendData[7];
-
- start = true;
- nameLen = 0;
- *numSubjects = 0;
- data = MyNewPtr(kBufLen);
- if (MyMemErr() != noErr)
- return MyMemErr();
-
- /* send GROUP <groupname> command */
-
- GiveTime(0);
-
- strcpy((char *)sendData[0],"GROUP ");
- strcpy((char *)sendData[1],newsGroup);
- strcpy((char *)sendData[2],CRLF);
-
- if ((err = SendMultiData(gConnID,sendData,3,true)) != noErr) {
- ResetConnection();
- return err;
- }
-
- GiveTime(0);
-
- length = (unsigned short) kBufLen;
- if ((err = RecvData(gConnID,data,&length,true)) != noErr) {
- ResetConnection();
- return err;
- }
-
- if (*data != '2') {
- ResetConnection();
- return -1;
- }
-
- /* send XHDR <hdrName> <first>-<last> command */
-
- GiveTime(0);
-
- strcpy((char *)sendData[0],"XHDR ");
- strcpy((char *)sendData[1],hdrName);
- strcpy((char *)sendData[2]," ");
- NumToString(first,(StringPtr)tmpStr);
- p2cstr(tmpStr);
- strcpy((char *)sendData[3],tmpStr);
- strcpy((char *)sendData[4],"-");
- NumToString(last,(StringPtr)tmpStr);
- p2cstr(tmpStr);
- strcpy((char *)sendData[5],tmpStr);
- strcpy((char *)sendData[6],CRLF);
-
- if ((err = SendMultiData(gConnID,sendData,7,true)) != noErr) {
- ResetConnection();
- return err;
- }
-
- GiveTime(0);
-
- while ( !done && ((err = RecvData(gConnID,data,&length,true)) == noErr)) {
- for (current = data; (current - data) < length; current++) {
- switch (*current) {
- case CR:
- if (state == cLastDot)
- state = cLastDotCR;
- else
- state = cLastCR;
- break;
- case LF:
- if (state == cLastCR) {
- state = cLastLF;
-
- if (nameLen > 250)
- nameLen = 250;
- curSubject[nameLen] = '\0';
-
- if (!start && curSubject[0] >= '0' && curSubject[0] <= '9' && (*numSubjects <= (last-first))) {
- subjects[*numSubjects].name = (char *) MyNewPtr(nameLen+2);
- if (MyMemErr() != noErr)
- return MyMemErr();
- strncpy(subjects[*numSubjects].name+1,curSubject,nameLen+1);
- *(subjects[*numSubjects].name) = ' ';
-
- for (current2 = numberStr,current3 = curSubject;
- *current3 != ' ' && *current3;
- *current2++ = *current3++)
- ;
- *current2 = '\0';
- StringToNum((StringPtr)c2pstr(numberStr),&articleNumber);
-
- subjects[*numSubjects].number = articleNumber;
- subjects[*numSubjects].read = false;
-
- (*numSubjects)++;
- StatusWindow("Getting Subject List...",(short)1+(((float)(1+*numSubjects)/(float)(last-first))*100));
- GiveTime(0);
-
- }
- else if (start && curSubject[0]!='2') {
- SysBeep(1);
- done = true;
- }
- else
- start = false;
-
- nameLen = 0;
- } else if (state == cLastDotCR) {
- state = cLastDotCRLF;
- done = true;
- }
- break;
- case '.':
- if (state == cLastLF)
- state = cLastDot;
- else {
- state = cNormal;
- curSubject[nameLen++] = *current;
- }
- break;
- default:
- state = cNormal;
- curSubject[nameLen++] = *current;
- break;
- }
- }
- length = (unsigned short) kBufLen;
- }
- MyDisposPtr(data);
- SetCursor(&QDARROW);
-
- if (err != noErr)
- ResetConnection();
-
- return noErr;
- }
-
-
- /* GetArticle gets the full text from one article from the NNTP server.
- This text is returned in a buffer which the routine allocates.
- */
-
- OSErr GetArticle(char *newsGroup,char *article,char **text,long *retLength,long maxLength)
- {
- OSErr err;
- Ptr data;
- unsigned short length;
- char *current;
- Boolean done,start;
- short state = cNormal;
- long totalLength = 0;
- Str255 sendData[3];
- Handle txtHndl;
- long curMaxLen;
-
- txtHndl = MyNewHandle(kMaxLength);
- if (MyMemErr() != noErr)
- return MyMemErr();
-
- curMaxLen = kMaxLength;
- HLock(txtHndl);
- *text = *txtHndl;
-
- start = true;
- done = false;
-
- data = MyNewPtr(kBufLen);
- if (MyMemErr() != noErr)
- return MyMemErr();
-
- if (newsGroup) {
- /* send GROUP <groupname> command */
-
- GiveTime(0);
-
- strcpy((char *)sendData[0],"GROUP ");
- strcpy((char *)sendData[1],newsGroup);
- strcpy((char *)sendData[2],CRLF);
-
- if ((err = SendMultiData(gConnID,sendData,3,true)) != noErr) {
- ResetConnection();
- return err;
- }
-
- GiveTime(0);
-
- length = (unsigned short) kBufLen;
- if ((err = RecvData(gConnID,data,&length,true)) != noErr) {
- ResetConnection();
- return err;
- }
-
- if (*data != '2') {
- ResetConnection();
- return -1;
- }
-
- }
-
- strcpy((char *)sendData[0],"ARTICLE ");
- strcpy((char *)sendData[1],article);
- strcpy((char *)sendData[2],CRLF);
-
- if ((err = SendMultiData(gConnID,sendData,3,true)) != noErr) {
- ResetConnection();
- return err;
- }
-
- length = kBufLen;
- while ( !done && ((err = RecvData(gConnID,data,&length,true)) == noErr)) {
- for (current = data; (current - data) < length; current++) {
- if (totalLength >= curMaxLen && totalLength < (maxLength-kMaxLength)) {
- curMaxLen += kMaxLength;
- HUnlock(txtHndl);
- MySetHandleSize(txtHndl,curMaxLen);
- if ((err = MyMemErr()) != noErr) {
- MyDisposHandle(txtHndl);
- MyDisposPtr(data);
- return err;
- }
-
- HLock(txtHndl);
- *text = *txtHndl;
- }
- switch (*current) {
- case CR:
- if (state == cLastDot)
- state = cLastDotCR;
- else
- state = cLastCR;
- if (!start) {
- if (totalLength < maxLength)
- (*text)[totalLength++] = *current;
- }
- break;
- case LF:
- if (state == cLastCR) {
- start = false;
- state = cLastLF;
- }
- else if (state == cLastDotCR) {
- state = cLastDotCRLF;
- done = true;
- }
- break;
- case '.':
- if (state == cLastLF)
- state = cLastDot;
- else if (!start) {
- state = cNormal;
- if (totalLength < maxLength)
- (*text)[totalLength++] = *current;
- }
- break;
- default:
- if (!start) {
- state = cNormal;
- if (totalLength < maxLength)
- (*text)[totalLength++] = *current;
- }
- else if (data[0]!='2')
- done = true;
- break;
- }
- }
- length = (unsigned short) kBufLen;
- GiveTime(0);
- }
- SetCursor(&QDARROW);
- *retLength = totalLength;
-
- MyDisposPtr(data);
-
- HUnlock(txtHndl);
- *text = MyNewPtr(totalLength);
- if ((err = MyMemErr()) != noErr) {
- MyDisposHandle(txtHndl);
- return err;
- }
- HLock(txtHndl);
- BlockMove(*txtHndl,*text,totalLength);
- HUnlock(txtHndl);
- MyDisposHandle(txtHndl);
-
- if (err != noErr)
- ResetConnection();
-
- return noErr;
- }
-
-
- /* SendNNTP posts a news article to a newsgroup using the 'POST' command
- on the NNTP server.
- */
-
- Boolean SendNNTP(char *text,unsigned short tLength)
- {
- extern unsigned long gConnID;
- Ptr data;
- unsigned short length;
- Boolean result;
- char commStr[256];
-
- strcpy(commStr,"POST");
- strcat(commStr,CRLF);
-
- if (SendData(gConnID,commStr,6,false) != noErr)
- return false;
-
- data = MyNewPtr(256);
- if (MyMemErr() != noErr)
- return false;
-
- strcpy(commStr,CRLF);
- strcat(commStr,".");
- strcat(commStr,CRLF);
-
- length = 200;
- if (RecvData(gConnID,data,&length,false) == noErr &&
- strncmp(data,"3",1)==0 &&
- SendData(gConnID,text,tLength,false) == noErr &&
- SendData(gConnID,commStr,5,false) == noErr &&
- (length = 200, true) &&
- RecvData(gConnID,data,&length,false) == noErr &&
- strncmp(data,"2",1)==0)
- result = true;
- else {
- result = false;
- }
- MyDisposPtr(data);
- return true;
- }
-
-
-